package com.daffodil.storage.service;

import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.UUID;

import javax.servlet.http.HttpServletRequest;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.web.multipart.MultipartFile;

import com.daffodil.storage.exception.BaseException;
import com.daffodil.storage.model.MediaAllow;
import com.daffodil.storage.model.MediaMimetype;
import com.daffodil.storage.model.StorageRequest;
import com.daffodil.storage.model.StorageResult;
import com.daffodil.storage.util.StorageUtils;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import lombok.extern.slf4j.Slf4j;

/**
 * 
 * @author yweijian
 * @date 2022年12月19日
 * @version 2.0.0
 * @description
 */
@Slf4j
public abstract class AStorageService implements IStorageService {

    @Override
    public StorageResult upolad(StorageRequest storageRequest) {
        if(ObjectUtil.isNotNull(storageRequest.getFile())) {
            return this.uploadByFile(storageRequest);
        }else if(ObjectUtil.isNotEmpty(storageRequest.getSource())) {
            return this.uploadBySource(storageRequest);
        }else {
            return this.upoladByBinary(storageRequest);
        }
    }

    /**
     * -本地文件上传
     * @param storageRequest
     * @return
     */
    private StorageResult uploadByFile(StorageRequest storageRequest) {
        MultipartFile multipartFile = storageRequest.getFile();
        if (null == multipartFile) {
            throw new BaseException(18001);
        }
        InputStream inputStream = null;
        try {
            inputStream = multipartFile.getInputStream();
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            throw new BaseException(18002);
        }

        StorageResult result = this.bulidStorageResult(storageRequest, multipartFile.getOriginalFilename(), multipartFile.getSize(), multipartFile.getContentType());

        return this.upolad(result, inputStream, storageRequest.getAllow());
    }

    /**
     * -下载远程网络地址文件并上传
     * @param storageRequest
     * @return
     */
    private StorageResult uploadBySource(StorageRequest storageRequest) {
        String source = storageRequest.getSource();
        if (ObjectUtil.isEmpty(source)) {
            throw new BaseException(18004);
        }
        boolean matches = source.matches("^(https|http):\\/\\/.*?");
        if(!matches) {
            throw new BaseException(18004);
        }
        HttpResponse httpResponse = HttpRequest.get(source).timeout(30000).execute();
        if (HttpStatus.OK.value() != httpResponse.getStatus()) {
            throw new BaseException(18002);
        }
        InputStream inputStream = httpResponse.bodyStream();
        Long size = 0L;
        try {
            size = Long.valueOf(inputStream.available());
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            throw new BaseException(18002);
        }

        StorageResult result = this.bulidStorageResult(storageRequest, source, size, httpResponse.header(HttpHeaders.CONTENT_TYPE));

        return this.upolad(result, inputStream, storageRequest.getAllow());
    }

    /**
     * -二进制流式上传
     * @param storageRequest
     * @return
     */
    private StorageResult upoladByBinary(StorageRequest storageRequest) {
        InputStream inputStream = null;
        HttpServletRequest request = storageRequest.getRequest();
        try {
            inputStream = request .getInputStream();
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            throw new BaseException(18002);
        }
        Long size = 0L;
        try {
            size = Long.valueOf(inputStream.available());
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            throw new BaseException(18002);
        }

        String fileName = storageRequest.getKey();
        if(ObjectUtil.isEmpty(storageRequest.getKey())) {
            String prefix = UUID.randomUUID().toString().replace(StrUtil.DASHED, StrUtil.EMPTY);
            String suffix = MediaMimetype.getInstance().getExtension(request.getContentType());
            fileName = prefix + (ObjectUtil.isNotEmpty(suffix) ? StrUtil.DOT + suffix : StrUtil.EMPTY);
        }

        StorageResult result = this.bulidStorageResult(storageRequest, fileName, size, request.getContentType());

        return this.upolad(result, inputStream, storageRequest.getAllow());
    }

    /**
     * -构建对象存储结果
     * @param storageRequest
     * @param fileName
     * @param size
     * @param mime
     * @return
     */
    private StorageResult bulidStorageResult(StorageRequest storageRequest, String fileName, Long size, String mime) {
        if(ObjectUtil.isEmpty(storageRequest.getKey())) {
            String folder = storageRequest.getFolder();
            StorageUtils.verifyAllowFolder(folder);
            StringBuilder key = new StringBuilder();
            if (ObjectUtil.isNotEmpty(folder)) {
                if(folder.startsWith(StrUtil.SLASH)) {
                    folder = folder.substring(1);
                }
                key.append(folder);
                key.append(StrUtil.SLASH);
            }
            key.append(DateUtil.format(new Date(), "yyyy-MM-dd"));
            key.append(StrUtil.SLASH);

            String origin = FileNameUtil.getName(fileName);
            String prefix = UUID.randomUUID().toString().replace(StrUtil.DASHED, StrUtil.EMPTY);
            String suffix = FileNameUtil.getSuffix(origin);
            if(ObjectUtil.isEmpty(suffix)) {
                suffix = MediaMimetype.getInstance().getExtension(mime);
            }
            if(ObjectUtil.isEmpty(mime)) {
                mime = MediaMimetype.getInstance().getMimetype(suffix);
            }
            String name = prefix + (ObjectUtil.isNotEmpty(suffix) ? StrUtil.DOT + suffix : StrUtil.EMPTY);

            key.append(name);
            return StorageResult.builder().name(name).origin(origin).prefix(prefix).suffix(suffix).size(size).mime(mime).key(key.toString()).build();
        }else {
            String origin = FileNameUtil.getName(storageRequest.getKey());
            String prefix = FileNameUtil.getPrefix(storageRequest.getKey());
            String suffix = FileNameUtil.getSuffix(storageRequest.getKey());
            if(ObjectUtil.isEmpty(suffix)) {
                suffix = MediaMimetype.getInstance().getExtension(mime);
            }
            if(ObjectUtil.isEmpty(mime)) {
                mime = MediaMimetype.getInstance().getMimetype(suffix);
            }
            String name = FileNameUtil.getName(storageRequest.getKey());
            String key = storageRequest.getKey();
            if(key.startsWith(StrUtil.SLASH)) {
                key = key.substring(1);
            }
            return StorageResult.builder().name(name).origin(origin).prefix(prefix).suffix(suffix).size(size).mime(mime).key(key).build();
        }
    }

    protected StorageResult upolad(StorageResult result, InputStream inputStream, MediaAllow allow) {

        StorageUtils.verifyAllowKey(result.getKey());
        StorageUtils.verifyAllowSuffix(result.getSuffix(), allow.getSuffixs());
        StorageUtils.verifyAllowContentType(result.getMime(), allow.getContentTypes());
        StorageUtils.verifyAllowSize(result.getSize(), allow.getMin(), allow.getMax());

        return this.upolad(result, inputStream);
    }

    protected abstract StorageResult upolad(StorageResult result, InputStream inputStream);

}
